/**
 * @file key.h
 * @author chushang (email:chushangcs@163.com)
 * @brief 按键头文件，实现按键单击，双击，长按，持续长按，按下瞬间，释放瞬间，持续按住的操作检测
 *        如有错误，欢迎联系邮箱，感激不尽！
 *        调用Key_Register初始化按键，定时中断调用Key_Tick，在应用中调用Key_Check检查按键是否出现了相应的动作
 *
 * @version 1.3
 * @date 2025-06-03
 *
 * @copyright Copyright (c) 2025
 *
 * @note 使用时请使用KeyConfig初始化Key
 */

#ifndef __KEY_H
#define __KEY_H

#define DRIVER_INCLUDE "stm32f1xx_hal.h"
#include DRIVER_INCLUDE

#define GPIO_PORT_TYPE GPIO_TypeDef *
#define GPIO_PIN_TYPE  uint16_t


#define KEY_MAX_NUM 3 // 按键最多数量

enum _KeyState
{
    KEY_NOACTION,      // 按键无动作
    KEY_HOLD   = 0x01, // 按键按住
    KEY_UP     = 0x02, // 按键释放瞬间
    KEY_DOWN   = 0x04, // 按键按下瞬间
    KEY_LONG   = 0x08, // 长按
    KEY_REPEAT = 0x10, // 长按不放
    KEY_CLICK  = 0x20, // 单击
    KEY_DOUBLE = 0x40  // 双击
};
typedef enum _KeyState KeyState;

enum _KeyPinState
{
    KEY_RELEASE, // 按键被释放
    KEY_PRESSED  // 按键被按下
};
typedef enum _KeyPinState KeyPinState;

// Key按键结构体
typedef struct _Key
{
    uint8_t     mode;          // 按键状态机标志，用户不可见
    KeyState    state;         // 按键动作响应，用户不可见
    KeyPinState curr_state;    // 按键引脚当前电平，用户不可见
    KeyPinState prev_state;    // 按键引脚上次电平，用户不可见
    uint16_t    pressing_time; // 按键持续被按下的时长，单位ms， 用户不可见

    uint16_t long_min_time;         // 判断长按所需的最短时长，单位ms，需要用户初始化
    uint16_t double_click_interval; // 双击间隔，单位ms，需要用户初始化
    uint8_t  period;                // 按键执行的周期，单位ms，需要用户初始化

    GPIO_PORT_TYPE gpio_port; // 引脚端口，需要用户初始化
    GPIO_PIN_TYPE  gpio_pin;  // 引脚号，需要用户初始化

    /* 如果所有按键释放和按下的电平状态均一致，那么该函数指针可以不用；
       否则代表个别按键需要个性化读取电平状态 */
    KeyPinState (*ReadPinState)(struct _Key *k); // 读取按键引脚电平状态的函数，需要用户初始化
} Key;

// 用户使用KeyConfig结构体初始化Key结构体，因此你需要确保该结构体的所有成员都是合法且有效的
typedef struct _KeyConfig
{
    uint16_t long_min_time;         // 判断长按所需的最短时长，单位ms
    uint16_t double_click_interval; // 双击间隔

    GPIO_TypeDef *gpio_port; // 引脚端口
    uint16_t      gpio_pin;  // 引脚号
    uint8_t       period;    // 检测按键的周期，单位ms

    KeyPinState (*ReadPinState)(struct _Key *k); // 读取按键引脚电平状态的函数
} KeyConfig;

Key        *Key_Register(KeyConfig *kc);
void        Key_Tick(void);
KeyPinState Key_ReadPin(Key *k);
uint8_t     Key_Check(Key *k, KeyState ks);
void        Key_Clear(Key *k);

#endif // !__KEY_H